'''
This file is part of ModeliMark.

ModeliMark is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.

ModeliMark is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with ModeliMark.
If not, see <http://www.gnu.org/licenses/>.

Copyright 2012 Jens Frenkel
'''

from modelimark.wrapper import BaseWrapper
import numbers
import shlex, subprocess
import os
from time import *

import win32api,win32process,win32con

class Dymola(BaseWrapper):
    '''
    classdocs
    '''


    def __init__(self,dymolapath,savelog=False):
        '''
        Constructor

        :param dymolapath: Path to the Dymola executable.
        :type dymolapath: String.
        '''

        assert(isinstance(dymolapath,str))
        self._dymola = dymolapath
        self._savelog = savelog

    def open(self, model, files, libs):
        '''
        instructs the compiler to open the Modelica
        code and return, the implementation is only necesarry
        if the compiler uses a window

        :param model: Name of the Model to flatten.
        :type model: String.

        :param files: all Files of the Model.
        :type files: list of String.
        '''
        assert(isinstance(model,str))
        assert(isinstance(files,list))

        # generate mos file
        mosfile = '%s_open.mos'%(model)
        f = open( mosfile, 'w' )
        for file in files:
            f.write('openModel("%s");\n'%(file))
        if (self._savelog):
            f.write('savelog("s_open.log")\n'%(model));
        f.write('exit();\n')
        # Close File
        f.close()

        #
        logfile = '%s_open_py.log'%(model)
        res, dt = self._run(logfile,[mosfile])
        if (res == 0):
            res = self._readResult(logfile)
        else:
            res = False

        return res, dt

    def flatten(self, model, files, libs):
        '''
        instructs the compiler to parse the Modelica
        code and return the flat model

        :param model: Name of the Model to flatten.
        :type model: String.

        :param files: all Files of the Model to flatten.
        :type files: list of String.
        '''
        assert(isinstance(model,str))
        assert(isinstance(files,list))

        # generate mos file
        mosfile = '%s_flatten.mos'%(model)
        f = open( mosfile, 'w' )
        for file in files:
            f.write('openModel("%s");\n'%(file))
        f.write('checkModel("%s");\n'%(model))
        if (self._savelog):
            f.write('savelog("s_flatten.log")\n'%(model));
        f.write('exit();\n')
        # Close File
        f.close()

        #
        logfile = '%s_flatten_py.log'%(model)
        res, dt = self._run(logfile,[mosfile])
        if (res == 0):
            res = self._readResult(logfile)
        else:
            res = False

        return res, dt

    def translate(self, model, files, libs):
        '''
        flattens the model and turns it into a state which can be simulated

        :param model: Name of the Model to flatten.
        :type model: String.

        :param files: all Files of the Model to flatten.
        :type files: list of String.
        '''
        assert(isinstance(model,str))
        assert(isinstance(files,list))

        # generate mos file
        mosfile = '%s_translate.mos'%(model)
        f = open( mosfile, 'w' )
        for file in files:
            f.write('openModel("%s");\n'%(file))
        f.write('translateModel("%s");\n'%(model))
        if (self._savelog):
            f.write('savelog("s_translate.log")\n'%(model));
        f.write('exit();\n')
        # Close File
        f.close()

        #
        logfile = '%s_translate_py.log'%(model)
        res, dt = self._run(logfile,[mosfile])
        if (res == 0):
            res = self._readResult(logfile)
        else:
            res = False

        return res, dt

    def simulate(self, model, files, libs, startTime, outputInterval, stopTime):
        '''
        flattens, translates and simulates the model using the standard solver
        and a predefined output interval and stop time

        :param model: Name of the Model to flatten.
        :type model: String.

        :param files: all Files of the Model to flatten.
        :type files: list of String.

        :param startTime: Start Time of the Simulation
        :type startTime: numbers.Real.

        :param outputInterval: Intervall for Output of Results.
        :type outputInterval: numbers.Real.

        :param stopTime: Stop Time of the Simulation
        :type stopTime: numbers.Real.
        '''
        assert(isinstance(model,str))
        assert(isinstance(files,list))
        assert(isinstance(startTime,numbers.Real))
        assert(isinstance(outputInterval,numbers.Real))
        assert(isinstance(stopTime,numbers.Real))

        # generate mos file
        mosfile = '%s_simulate.mos'%(model)
        f = open( mosfile, 'w' )
        for file in files:
            f.write('openModel("%s");\n'%(file))
        f.write('simulateModel("%s", startTime=%s, stopTime=%s, numberOfIntervals=0, outputInterval=%s);\n'%(model,str(startTime),str(stopTime),str(outputInterval)))
        if (self._savelog):
            f.write('savelog("s_simulate.log")\n'%(model));
        f.write('exit();\n')
        # Close File
        f.close()

        #
        logfile = '%s_simulate_py.log'%(model)
        res, dt = self._run(logfile,[mosfile])
        if (res == 0):
            res = self._readResult(logfile)
        else:
            res = False

        return res, dt


    def _run(self,logfilename,args,cwd=None):

        assert(isinstance(logfilename,str))

        ar = list()
        ar.append(self._dymola)
        for a in args:
            ar.append(a)

        print 'starte Dymola'
        f = open( logfilename, 'w' )
        t1 = clock()
        p = subprocess.Popen(args=ar,stdout=f,cwd=cwd)

        pid = p.pid
        handle = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, True, pid)
        win32process.SetPriorityClass(handle, win32process.REALTIME_PRIORITY_CLASS)

        run = True
        while run:
            res = p.poll()
            if (res is not None):
                run = False
        t2 = clock()
        dt = t2 - t1
        f.close()
        return res, dt

    def _readResult(self,logfilename):

        with open(logfilename, 'r') as f:
            for line in f:
                if (line == ' = false\n'):
                    return False
#                elif (line == ' = true\n'):
#                    pass
#                else:
#                    print "ignoring %s"%line
        f.close()
        return True